#define ZGRAPHICS_SOURCE
#include "../Graphics/OpenGLTools.hpp"
#include "../Graphics/Graphics.hpp"
#include "../Renderer/Renderer.hpp"
#include "Context.hpp"

namespace zzz{
Context *Context::current_context_=NULL;
#ifdef ZZZ_OPENGL_MX
Mutex Context::OpenGLMutex;
#endif // ZZZ_OPENGL_MX

Context::Context()
:own_mouse_(false),own_keyboard_(false),
gui_(NULL),renderer_(NULL),show_gui_(true)
{}

Context::~Context()
{
  RM_.Clear();
  if (current_context_==this)
    current_context_=NULL;
}

void Context::SetRenderer(Renderer *renderer)
{
  renderer_=renderer;
  renderer->SetContext(this);
  if (renderer_->gui_) gui_=renderer_->gui_;
}

void Context::RenderScene()
{
  // Always lock the RenderScene, so context won't be switched in the middle of rendering.
  // There is only one graphics card anyway, so it won't affect the performance much.
  // Just don't write too much non-rendering stuff inside RenderScene.
#ifdef ZZZ_OPENGL_MX
  OpenGLMutex.Lock();
#endif // ZZZ_OPENGL_MX
  MakeCurrent();
  renderer_->RenderScene();
  if (gui_ && show_gui_) gui_->Draw();
  SwapBuffer();
#ifdef ZZZ_OPENGL_MX
  OpenGLMutex.Unlock();
#endif // ZZZ_OPENGL_MX
}

void Context::SwapRenderer(Context *other)
{
  if (other==this) return;

  Renderer *r=renderer_;
  renderer_=other->renderer_;
  other->renderer_=r;
  renderer_->SetContext(this);
  r->SetContext(other);

  MakeCurrent();
  renderer_->OnSize(0,width_,height_);
  other->MakeCurrent();
  r->OnSize(0,other->width_,other->height_);
}

void Context::MakeCurrent()
{
  current_context_=this;
}

void Context::OnMouseMove(unsigned int nFlags,int x,int y)
{
  if (!own_mouse_ && gui_ && show_gui_ &&
    gui_->OnMouseMove(nFlags, x, y)) return;
  renderer_->OnMouseMove(nFlags, x, y);
}

void Context::OnLButtonDown(unsigned int nFlags,int x,int y)
{
  if (!own_mouse_ && gui_ && show_gui_ &&
    gui_->OnLButtonDown(nFlags, x, y)) return;
  renderer_->OnLButtonDown(nFlags, x, y);
}

void Context::OnLButtonUp(unsigned int nFlags,int x,int y)
{
  if (!own_mouse_ && gui_ && show_gui_ &&
    gui_->OnLButtonUp(nFlags, x, y)) return;
  renderer_->OnLButtonUp(nFlags, x, y);
  OwnMouse(false);
}

void Context::OnRButtonDown(unsigned int nFlags,int x,int y)
{
  if (!own_mouse_ && gui_ && show_gui_ && 
    gui_->OnRButtonDown(nFlags, x, y)) return;
  renderer_->OnRButtonDown(nFlags, x, y);
}

void Context::OnRButtonUp(unsigned int nFlags,int x,int y)
{
  if (!own_mouse_ && gui_ && show_gui_ &&
    gui_->OnRButtonUp(nFlags, x, y)) return;
  renderer_->OnRButtonUp(nFlags, x, y);
  OwnMouse(false);
}

void Context::OnMButtonDown(unsigned int nFlags,int x,int y)
{
  if (!own_mouse_ && gui_ && show_gui_ &&
    gui_->OnMButtonDown(nFlags, x, y)) return;
  renderer_->OnMButtonDown(nFlags, x, y);
}

void Context::OnMButtonUp(unsigned int nFlags,int x,int y)
{
  if (!own_mouse_ && gui_ && show_gui_ &&
    gui_->OnMButtonUp(nFlags, x, y)) return;
  renderer_->OnMButtonUp(nFlags, x, y);
  OwnMouse(false);
}

void Context::OnMouseWheel(unsigned int nFlags, int zDelta, int x,int y)
{
  if (!own_mouse_ && gui_ && show_gui_ &&
    gui_->OnMouseWheel(nFlags, zDelta, x, y)) return;
  renderer_->OnMouseWheel(nFlags, zDelta, x, y);
}

void Context::OnSize(unsigned int nType, int cx, int cy)
{
  if (gui_) gui_->OnSize(0, cx, cy);
  width_=cx;height_=cy;
  renderer_->OnSize(nType, cx, cy);
}

void Context::OnChar(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags)
{
  if (!own_keyboard_ && gui_ && show_gui_ &&
    gui_->OnChar(nChar, nRepCnt, nFlags)) return;
  renderer_->OnChar(nChar, nRepCnt, nFlags);
}

void Context::OnKeyDown(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags)
{
  if (nChar==ZZZKEY_CAPSLOCK){
    show_gui_=!show_gui_;
    Redraw();
    return;
  }

  if (!own_keyboard_ && gui_ && show_gui_ &&
    gui_->OnKeyDown(nChar, nRepCnt, nFlags)) return;
  renderer_->OnKeyDown(nChar, nRepCnt, nFlags);
}

void Context::OnKeyUp(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags)
{
  if (!own_keyboard_ && gui_ && show_gui_ &&
    gui_->OnKeyUp(nChar, nRepCnt, nFlags)) return;
  renderer_->OnKeyUp(nChar, nRepCnt, nFlags);
}

void Context::OnIdle()
{
  renderer_->OnIdle();
}

}